// src/tcp-client.ts
// TCP客户端类，用于建立和管理与TCP服务器的连接

// 导入Node.js内置的net模块，用于创建TCP连接
import * as net from 'net';
// 导入消息分隔符常量，用于分割接收到的数据包
import { DELIMITER } from './constants';

/**
 * TCP客户端类
 * 提供TCP连接的建立、数据收发、自动重连等功能
 */
export class TcpClient {
  // TCP Socket连接对象，null表示未连接
  private client: net.Socket | null = null;
  // 目标服务器的主机地址（IP或域名）
  private host: string;
  // 目标服务器的端口号
  private port: number;
  // 是否启用自动重连功能，默认为true
  private autoReconnect: boolean = true;
  // 重连定时器的引用，用于定期尝试重连
  private reconnectInterval: NodeJS.Timeout | null = null;
  // 连接状态标志，true表示已连接，false表示未连接
  private isConnected: boolean = false;
  // 接收数据的缓冲区，用于处理不完整的数据包
  private incomingBuffer: string = '';

  /**
   * 构造函数
   * @param host 服务器主机地址
   * @param port 服务器端口号
   */
  constructor(host: string, port: number) {
    // 保存服务器地址和端口
    this.host = host;
    this.port = port;
  }

  /**
   * 建立TCP连接
   * 如果已经连接则不重复连接，否则创建新的Socket连接
   */
  public connect(): void {
    // 检查是否已经存在连接且未被销毁
    if (this.client && !this.client.destroyed) {
      console.log('TCP: 已经连接，无需重复连接。');
      return;
    }
    // 输出连接尝试日志
    console.log(`TCP: 正在尝试连接 ${this.host}:${this.port}...`);
    // 创建新的TCP Socket对象
    this.client = new net.Socket();
    // 发起TCP连接，连接成功后执行回调函数
    this.client.connect(this.port, this.host, () => {
      // 设置连接状态为已连接
      this.isConnected = true;
      console.log(`TCP: 连接成功！`);
      // 如果存在重连定时器，清除它（因为已经连接成功）
      if (this.reconnectInterval) {
        clearInterval(this.reconnectInterval);
        this.reconnectInterval = null;
      }
      // 触发'ready'事件，通知外部连接已就绪
      this.client?.emit('ready');
    });

    // 监听数据接收事件
    this.client.on('data', (data) => {
      // 将接收到的Buffer数据转换为UTF-8字符串
      const decodedData = data.toString('utf-8');
      // 将新数据追加到接收缓冲区
      this.incomingBuffer += decodedData;
      // 处理缓冲区中的数据，提取完整的消息
      this.processIncomingData();
    });

    // 监听连接关闭事件
    this.client.on('close', (hadError) => {
      console.log(`TCP: 连接已断开。${hadError ? '(由于错误)' : ''}`);
      // 更新连接状态
      this.isConnected = false;
      this.client = null;
      // 如果启用了自动重连，开始重连流程
      if (this.autoReconnect) {
        this.startReconnect();
      }
      // 触发'disconnected'事件，通知外部连接已断开
      this.client?.emit('disconnected');
    });

    // 监听连接错误事件
    this.client.on('error', (err) => {
      console.error('TCP: 连接错误:', err.message);
      console.error('TCP: 错误详情:', err);
      // 更新连接状态
      this.isConnected = false;
      // 销毁Socket连接
      this.client?.destroy();
    });
  }

  /**
   * 处理接收到的数据
   * 根据分隔符将缓冲区中的数据分割成完整的消息
   */
  private processIncomingData(): void {
    // 使用分隔符分割缓冲区数据
    let parts = this.incomingBuffer.split(DELIMITER);
    
    // 当分割后的数组长度大于1时，说明至少有一个完整的消息
    while (parts.length > 1) {
      // 取出第一个完整的消息（不包含分隔符）
      const message = parts.shift();
      // 如果消息不为空，触发'message'事件并重新添加分隔符
      if (message) {
        this.client?.emit('message', message + DELIMITER);
      }
      // 将剩余的部分重新组合成缓冲区内容
      this.incomingBuffer = parts.join(DELIMITER);
    }
  }

  /**
   * 启动自动重连机制
   * 设置定时器，每5秒检查一次连接状态并尝试重连
   */
  private startReconnect(): void {
    // 如果重连定时器已存在，直接返回
    if (this.reconnectInterval) return;
    // 设置定时器，每5秒执行一次
    this.reconnectInterval = setInterval(() => {
      // 如果当前未连接，尝试重新连接
      if (!this.isConnected) {
        this.connect();
      }
    }, 5000); // 每5秒尝试重连一次
  }

  /**
   * 发送数据到服务器
   * @param data 要发送的字符串数据
   */
  public send(data: string): void {
    // 检查连接是否存在且已连接
    if (this.client && this.isConnected) {
      console.log(`[debug]TCP发送数据: ${data}`);
      // 将字符串转换为Buffer，确保编码正确
      const buffer = Buffer.from(data, 'utf-8');
      this.client.write(buffer);
    } else {
      // 如果未连接，输出警告信息
      console.warn('TCP: 未连接，无法发送数据。');
    }
  }

  /**
   * 断开TCP连接
   * 清理重连定时器，禁用自动重连，并关闭Socket连接
   */
  public disconnect(): void {
    // 清除重连定时器
    if (this.reconnectInterval) {
      clearInterval(this.reconnectInterval);
      this.reconnectInterval = null;
    }
    // 禁用自动重连功能
    this.autoReconnect = false;
    // 优雅地关闭Socket连接
    this.client?.end();
  }

  /**
   * 注册事件监听器
   * @param event 事件名称
   * @param listener 事件处理函数
   */
  public on(event: string, listener: (...args: any[]) => void): void {
    // 将事件监听器注册到Socket对象上
    this.client?.on(event, listener);
  }

  /**
   * 获取当前连接状态
   * @returns 返回true表示已连接，false表示未连接
   */
  public get isConnectedToServer(): boolean {
    return this.isConnected;
  }
}